package pocisk;

import java.util.ArrayList;

/**
 *
 * @author Maciej
 */
public class Rosenbruck {

    double eps; //dokładność
    ArrayList<MyPoint2D> x; //punkty wynikowe 'błądzenia', x0 - punkt startowy
    ArrayList<MyPoint2D> z; //obecnie ustawione zm decyz (3 punkty dla 2 wymiarów - 1,2 do kroku próbnego, 3 do kroku roboczego i końca)
    MyPoint2D[] d; //baza kierunkow ortogonalnych
    MyPoint2D drobocze; //kierunek do kroku roboczego
    int s; //obecny wymiar
    static final int S = 2; //limit wymiarów
    int n; //ilosc wykonanych kroków roboczych
    HitTargetFunction F; //interfejs funkcji celu z metodą function()
    double tau; //obecna długość kroku
    public int STATE; //stan
    public static final int STATE_TRYING = 1; //krok próbny
    public static final int STATE_WORKING = 2; //krok roboczy
    int maxi; //maks iteracji
    //koniec metody numerycznej - odległość pomiędzy dwoma ostatnimi punktami błądzenia jest mniejsza od eps lub przekroczono maksymalną ilość iteracji
    double [][]d_t={{1,0},{0,1}};
    Matrix dj=new Matrix(d_t);



    Rosenbruck() {
    }

    public void compute(double[] start, double epsilon, HitTargetFunction function, int maxiter) {
        STATE = STATE_TRYING;
        maxi = maxiter;
        x = new ArrayList<MyPoint2D>();
        z = new ArrayList<MyPoint2D>();
        eps = epsilon;
        x.add(0, new MyPoint2D(start[0], start[1])); //dodaj pierwszy punkt błądzenia
        F = function;
        etap_0();
        do {
            etap_1();
        } while (STATE == STATE_WORKING); //dopóki nie został spełnion warunek stopu
        /*
        System.out.println("Punkty po drodze:");
        for(MyPoint2D el: x){
        System.out.println(el);
        }
         */
    }

    public double[] result() {
        MyPoint2D ost = x.get(x.size() - 1);
        double[] res = {ost.getX(), ost.getY()};
        return res;
    }

    public void etap_0() { //etap przygotowawczy
        MyPoint2D[] baza = {new MyPoint2D(1d, 0d), new MyPoint2D(0d, 1d)};
        d = baza;
        n = 0;
        s = 0;
        z.add(0, x.get(0).clone());
    }

    public void etap_1() { //etap próbny - krok w jednym wymiarze o optymalnej długości tau
        STATE = STATE_TRYING;
        znajdzTau();
        z.add(s + 1, z.get(s).PointSum(d[s].ValueMult(tau)));
        etap_2();

    }

    public void etap_2() { //sprawdź czy kroki we wszystkich wymiarach zostały wykonane w etapie próbnym
        if (s < (S - 1)) {
            s++;
            etap_1();
        } else {
            etap_3();
        }
    }

    public void etap_3() { //przygotowanie do etapu roboczego, sprawdzenie warunku stopu
        x.add(n + 1, z.get(s + 1).clone());
        if (maxi > n && x.get(n + 1).distance(x.get(n)) >= eps) { //czy przekroczono iteracje bądź osiągnieto kryterium zbieżności
            STATE = STATE_WORKING;
            etap_roboczy();
        }
    }

    public void etap_roboczy() {
        drobocze = d [0];
        z.set(0, x.get(n + 1));
        s=0;
        n++;
        drobocze = (x.get(n + 1).PointSub(x.get(n))).ValueDiv(x.get(n + 1).distance(x.get(n)));
        znajdzTau();
        Matrix d2=new Matrix(2,2);
                                Matrix aj=null;
                                
                                for(int i=0;i<2;i++){
                                        
                                        if(tau==0){
                                                aj=dj.getSubMatrixCol(i);
                                        }else{
                                                //System.out.println("T"+A2[0]+" "+A2[1]);
                                                aj=dj.getSubMatrixCol(0).times(tau);
                                                aj=aj.plus(dj.getSubMatrixCol(1).times(tau));
                                        }

                                        //if(i==1)aj=aj.transpose();
                                        d2.setValue(i, 0, aj.getValue(0, (i==0?0:1)));
                                        d2.setValue(i, 1, aj.getValue(0, (i==0?1:0)));
                                }
                                d2.show();
                                d2=d2.normalize();
                                MyPoint2D[] bazapomocnicza = {new MyPoint2D(d2.getValue(0, 0), d2.getValue(0, 1)), new MyPoint2D(d2.getValue(1, 0), d2.getValue(1, 1))};
                                d=bazapomocnicza;
                                
    }

    public void znajdzTau() {
        Minimum1DRos mrange = new Minimum1DRos(this);
        double guess = 20;
        double startmove = 40;
        double minmove = 0.5;
        int maxiter = 30;
        double epsilon = 0.2;
        mrange.gradcompute(guess, startmove, minmove, maxiter, epsilon);
        tau = mrange.result;
    }

    double callFunction(double x, double y) {
        double[] crit = {x, y};
        return F.function(crit);
    }

    double callFunction(MyPoint2D p) {
        double[] crit = {p.getX(), p.getY()};
        return F.function(crit);
    }

    double callTau(double tau) { //pomaga Minimum1D w odwołaniach do funkcji celu - redukuje funkcje 2D do 1D ustawiając obecnie potrzebne współczynniki(w zależności czy jest to tau do etapu próbnego czy roboczego)
        MyPoint2D arg;
        if (STATE == STATE_WORKING) {
            arg = x.get(n + 1).PointSum(drobocze.ValueMult(tau));
        } else {
            arg = z.get(s).PointSum(d[s].ValueMult(tau));
        }
        return callFunction(arg);
    }
}
